#include "defines.h"
#include "lcd.h"
#include "string.h"

// return mimeType according to filename
// using mstrlen mstrcmp
char *getType(char *filename) {
    u32 i, length;
    char *ext;
	  length = mstrlen(filename);
    for (i = length; i > 0; i--) {
        if (filename[i] == '.') break;
    }
    ext = (i) ? &(filename[i]) : "unknown";
    if (mstrcmp(ext, ".js") == 0) return "text/javascript";
    if (mstrcmp(ext, ".css") == 0) return "text/";
    if (mstrcmp(ext, ".html") == 0) return "text/html";
    if (mstrcmp(ext, ".txt") == 0) return "text/plain";
    if (mstrcmp(ext, ".pdf") == 0) return "application/pdf";
    if (mstrcmp(ext, ".jpeg") == 0) return "image/jpeg";
    if (mstrcmp(ext, ".jpg") == 0) return "image/jpeg";
    if (mstrcmp(ext, ".png") == 0) return "image/png";
    if (mstrcmp(ext, ".svg") == 0) return "image/svg+xml";
    if (mstrcmp(ext, ".ico") == 0) return "image/x-icon";
    return "";
}

// WARN: response malloced, but not free
// WARN: ifile.data freed here
// FUNC: write response
// USE : mstrcmp mstrcpy mstrcat readFile mstrlen mmemcpy num2str mmalloc mfree getType
char *sendFile(char *response, char *reqURL) {
    // 0D 0A: \r\n
    // http: header\r\n\r\nbody
    u32 headerSize = 0, bodySize = 0;
    char realPath[256] = "";
    char resHeader[512] = "";        // max resHeader is 512 bytes
    char contentLengthStr[12] = "";  // max content-length is 999,999,999,999 bytes
    file ifile;
	  memset(realPath,0,256);
	  memset(resHeader,0,512); 
	  memset(contentLengthStr,0,12);
    if (mstrcmp(reqURL, "/") == 0) {  // 302 FOUND
        mstrcpy(resHeader, "HTTP/1.1 302 Found\r\n");
        mstrcat(resHeader, "Location: /");
        mstrcat(resHeader, indexPage);
        mstrcat(resHeader, "\r\n");
        mstrcat(resHeader, "Connection: keep-alive\r\n");
        mstrcat(resHeader, "Cache-Control: public, max-age=0\r\n");
        mstrcat(resHeader, "Content-Length: 0\r\n");
        mstrcat(resHeader, "\r\n");
        headerSize = mstrlen(resHeader);
        response = (char *)mmalloc(headerSize + 1);
        response[headerSize] = '\0'; // make sure have '\0'
        mmemcpy(response, resHeader, headerSize);
        return response;
    }

    mstrcpy(realPath, root);
    mstrcat(realPath, reqURL);
		LCD_DisplayString(20,120,16,(u8 *)realPath);
    ifile = readFile(realPath);
    if (ifile.size == 0) {  // 404 NOT FOUND
        bodySize = mstrlen(page404);
        num2str(bodySize, contentLengthStr, 10);
        mstrcpy(resHeader, "HTTP/1.1 404 Not Found\r\n");
        mstrcat(resHeader, "Content-Type: text/html; charset=utf-8\r\n");
        mstrcat(resHeader, "Connection: keep-alive\r\n");
        mstrcat(resHeader, "Cache-Control: public, max-age=0\r\n");
        mstrcat(resHeader, "Content-Length: ");
        mstrcat(resHeader, contentLengthStr);
        mstrcat(resHeader, "\r\n");
        mstrcat(resHeader, "\r\n");
        headerSize = mstrlen(resHeader);
        response = (char *)mmalloc(headerSize + bodySize + 1);
        mmemcpy(response, resHeader, headerSize);
        mmemcpy((char *)(response + headerSize), page404, bodySize);
        response[headerSize + bodySize] = '\0'; // make sure have '\0'
        return response;
    }

    bodySize = ifile.size;  // 200 OK
    num2str(bodySize, contentLengthStr, 10);
    mstrcpy(resHeader, "HTTP/1.1 200 OK\r\n");
    mstrcat(resHeader, "Content-Type: ");
    mstrcat(resHeader, getType(reqURL));
    mstrcat(resHeader, "; charset=utf-8\r\n");
    mstrcat(resHeader, "Connection: keep-alive\r\n");
    mstrcat(resHeader, "Cache-Control: public, max-age=0\r\n");
    mstrcat(resHeader, "Content-Length: ");
    mstrcat(resHeader, contentLengthStr);
    mstrcat(resHeader, "\r\n");
    mstrcat(resHeader, "\r\n");
    headerSize = mstrlen(resHeader);
    response = (char *)mmalloc(headerSize + bodySize + 1);
    mmemcpy(response, resHeader, headerSize);
    mmemcpy((char *)(response + headerSize), ifile.data, bodySize);
    response[headerSize + bodySize] = '\0'; // make sure have '\0'
    mfree(ifile.data);
    return response;
}

// WARN: response and inf.dataBody malloced, but not mfree
// FUNC: write response, write info
// USE : mstrlen mmemcpy mstrcat num2str str2num mmalloc
char *getPost(char *response, u32 postLength, char *postBody, info *inf) {
    int i;
	  u32 headerSize = 0, bodySize = 0;
    char endSign[8];
    char contentLengthStr[12] = "";  // max content-length is 999,999,999,999 bytes
    char resHeader[512] = "";        // max resHeader is 512 bytes
    // write info ==================================//
    if (postLength < 6) {  // min length is 6
        inf->error = 1;
    } else {
        inf->error = 0;
        inf->isParameter = (postBody[0] == 'P');
        inf->isParameter = inf->isParameter && (postBody[1] == 'A');
        inf->isParameter = inf->isParameter && (postBody[2] == 'R');
        inf->isParameter = inf->isParameter && (postBody[3] == 'A');
        if (inf->isParameter) {
            inf->rRate = postBody[4];
            inf->dRate = postBody[5];
            inf->dataLength = 0;
            inf->dataBody = NULL;
        } else {
            inf->rRate = 0;
            inf->dRate = 0;
            mmemcpy(&(inf->dataLength), &(postBody[4]), 4);
            if ((inf->dataLength * 4 + 14) != postLength) {  // not consist with contentLength
                inf->error = 1;
            } else {
                if (inf->dataBody)
                    mmemcpy(inf->dataBody, &(postBody[8]), inf->dataLength * 4);
                else
                    inf->error = 1;
                mmemcpy(endSign, &(postBody[inf->dataLength * 4 + 8]), 6);
                endSign[6] = '\0';
                if (mstrcmp(endSign, "\r\nEND\0") != 0)
                    inf->error = 1; 
                // printf("%s",endSign);  
            }
        }
    }
    // printf("->contentLength:%d\n",postLength);
    // printf("->%d_%d_%d_%d\n", postBody[4], postBody[5], postBody[6], postBody[7]);

    // write response =================================//
    mstrcpy(resHeader, "HTTP/1.1 200 OK\r\n");
    mstrcat(resHeader, "Content-Type: text/plain; charset=utf-8\r\n");
    mstrcat(resHeader, "Connection: keep-alive\r\n");
    mstrcat(resHeader, "Cache-Control: public, max-age=0\r\n");
    mstrcat(resHeader, "Content-Length: 0\r\n");
    mstrcat(resHeader, "\r\n");
    headerSize = mstrlen(resHeader);
    response = (char *)mmalloc(headerSize + 1);
    mmemcpy(response, resHeader, headerSize);
    response[headerSize] = '\0'; // make sure have '\0'
    return response;
}

// WARN: request->content freed here
// FUNC: decide to getPost or sendFile
// USE : getPost sendFile mstrstr mstrlen mmemcpy
message *procReq(message *request, message *response, info *inf) {
    u32 postLength;
    u32 headerLength;
    char *tmpStart, *tmpEnd;
    char reqURL[64];
    char *postBody;
    memset(reqURL,0,64);
	
    postBody = (char *)(mstrstr(request->content, "\r\n\r\n", request->size) + 4);
    headerLength = (u32)(postBody - request->content);
    // actually bodyLength can be calculated, so enough is ok 

    if (mstrstr(request->content, "POST ", headerLength) != NULL) {
        // Content-Length: 8\r\n
        postLength = 0;
        tmpStart = (char *)(mstrstr(request->content, "Content-Length:", headerLength) + 15);
        tmpEnd = mstrstr(tmpStart, "\r\n", 32);
        if ((tmpStart != NULL) && (tmpEnd != NULL)) {
            reqURL[(u32)(tmpEnd - tmpStart)] = '\0';
            mmemcpy(reqURL, tmpStart, (u32)(tmpEnd - tmpStart));
            postLength = str2num(reqURL);
        }
        // printf("->%d",postLength); // OK
        response->content = getPost(response->content, postLength, postBody, inf);
        response->size = mstrlen(response->content);
        return response;
    } else {
        // GET /... HTTP/1.1
        tmpStart = (char *)(request->content + 4);
        tmpEnd = mstrstr(request->content, " HTTP", headerLength);
        reqURL[0] = '/', reqURL[1] = '\0';
        if ((tmpStart != NULL) && (tmpEnd != NULL)) {
            reqURL[(u32)(tmpEnd - tmpStart)] = '\0';
            mmemcpy(reqURL, tmpStart, (u32)(tmpEnd - tmpStart));
        }
        // printf("->%s",reqURL); // OK				
        response->content = sendFile(response->content, reqURL);
        response->size = mstrlen(response->content);
        return response;
    }
//     mfree(request->content); // spec memory for request, no free 
}

// FUNC: read file using functions of ff.c
file readFile(char *filename) {
    file ifile;
    u32 status = 1, fileSize = 0;
    FIL readStream;
	  FRESULT res=1;
	  res = f_open(&readStream,filename, FA_OPEN_EXISTING|FA_READ);  // read only
    if (res) {               // error
			  LCD_DisplayString(20,60,16,"read failed");
        ifile.data = NULL;
        ifile.size = 0;
        return ifile;
    }
		ifile.data = (u8*)mmalloc(readStream.fsize);
//    getFileSize(&fileSize, readStream);
		fileSize = readStream.fsize;
    f_read(&readStream,ifile.data, fileSize, &status);
		
		f_close(&readStream);
    if (status != fileSize) {  // error
        ifile.data = NULL;
        ifile.size = 0;
        return ifile;
    }  
		LCD_DisplayString(20,60,16,"read successfully");
    ifile.size = fileSize;
    return ifile;
}

// FUNC: pre malloc request
message *preRequest(message *request) { // pre locate the request memory
    request->size = preMemSize;
    request->content = (char *)mmalloc(preMemSize);
    return request;
}

// FUNC: pre malloc info
info *preInfo(info *data) {
    data->isParameter = 0;
    data->rRate = 0;
    data->dRate = 0;
    data->error = 0;
    data->dataLength = 0;
    data->dataBody = (u16 *)mmalloc(preMemSize);
}
